import unit.Node;
import unit.Pair;
import unit.Part;
import unit.Word;

import java.util.ArrayList;
import java.util.Map;
import java.util.Stack;

public class Parser {
    private static final Parser parser = new Parser();
    private ArrayList<Word> wordList;
    private final Map<String, String> dict = Dict.returnDict();
    private final ArrayList<Part> partList = new ArrayList<>();
    private ArrayList<Pair<Integer, String>> errorList = new ArrayList<>();

    private String lookahead;
    private int index = -1;
    private int loop = 0; //标志当前的循环层数
    private int funcHasRet = -1;
    //标志当前是否在有返回值的函数中 1:有返回值  0:无返回值 -1:非函数

    private final Stack<Integer> ancestor = new Stack<>();
    private final Stack<Node> children = new Stack<>();

    private Parser() {
    }

    public static Parser getInstance() {
        return parser;
    }

    public ArrayList<Part> getPartList() {
        return partList;
    }

    public ArrayList<Pair<Integer, String>> getErrorList() {
        return errorList;
    }

    public Node getRoot() {
        return children.peek();
    }

    public void parserStart(ArrayList<Word> wordList) {
        this.wordList = wordList;
        next();
        CompUnit();
    }

    public void CompUnit() {
        ancestor.push(children.size());
        if (lookahead.equals("const")) {
            ConstDecl();
            CompUnit1();
        } else if (lookahead.equals("void")) {
            FuncDef();
            CompUnit1();
        } else if (lookahead.equals("int")) {
            if (read(index + 1).equals("main")) {
                MainFuncDef();
                mark("CompUnit");
            } else if (read(index + 2).equals("(")) {
                FuncDef();
                CompUnit1();
            } else {
                VarDecl();
                CompUnit1();
            }
        }
        if (children.size() != 2) {
            errorList = new ArrayList<>();
        }
        Node root = getRoot();
        ArrayList<Node> children = root.getChildren();
        for (int i = 0; i < children.size(); i++) {
            if (!(children.get(i).getType().equals("ConstDecl") ||
                    children.get(i).getType().equals("VarDecl") ||
                    children.get(i).getType().equals("FuncDef") ||
                    children.get(i).getType().equals("MainFuncDef"))) {
                errorList = new ArrayList<>();
                break;
            }
        }
    }

    public void CompUnit1() {
        if (lookahead.equals("const")) {
            ConstDecl();
            CompUnit1();
        } else if (lookahead.equals("void")) {
            FuncDef();
            CompUnit1();
        } else if (lookahead.equals("int")) {
            if (read(index + 1).equals("main")) {
                MainFuncDef();
                mark("CompUnit");
            } else if (read(index + 2).equals("(")) {
                FuncDef();
                CompUnit1();
            } else {
                VarDecl();
                CompUnit1();
            }
        }
    }

    public void FuncDef() {
        ancestor.push(children.size());
        if (read(index + 1).equals("IDENFR") && read(index + 2).equals("(")) {
            funcHasRet = lookahead.equals("int") ? 1 : 0;
            FuncType();
            next(2);
            if (lookahead.equals(")")) {
                next();
                Block();
            } else if (lookahead.equals("{")) {
                error(getRow(index - 1), "j");
                Block();
            } else {
                FuncFParams();
                if (lookahead.equals(")")) {
                    next();
                    Block();
                } else {
                    error(getRow(index - 1), "j");
                    Block();
                }
            }
            Node node1 = children.lastElement();
            Node node2 = node1.getChildren().get(node1.getChildren().size() - 2);
            if ((node2.getChildren().size() == 0 || node2.getChildren().size() > 0 &&
                    !node2.getChildren().get(0).getType().equals("return")) && funcHasRet == 1) {
                error(getRow(index - 1), "g");
            }
            mark("FuncDef");
        }
        funcHasRet = -1;
    }

    public void FuncType() {
        ancestor.push(children.size());
        if (lookahead.equals("void") || lookahead.equals("int")) {
            next();
            mark("FuncType");
        }
    }

    public void MainFuncDef() {
        ancestor.push(children.size());
        if (read(index + 2).equals("(")) {
            funcHasRet = 1;
            next(3);
            if (lookahead.equals(")")) {
                next();
            } else {
                error(getRow(index - 1), "j");
            }

            Block();
            Node node1 = children.lastElement();
            Node node2 = node1.getChildren().get(node1.getChildren().size() - 2);
            if ((node2.getChildren().size() == 0 || node2.getChildren().size() > 0 &&
                    !node2.getChildren().get(0).getType().equals("return"))) {
                error(getRow(index - 1), "g");
            }
            mark("MainFuncDef");
        }
        funcHasRet = -1;
    }

    public void Block() {
        ancestor.push(children.size());
        if (lookahead.equals("{")) {
            next();
            while (!lookahead.equals("}")) {
                if (lookahead.equals("const")) {
                    ConstDecl();
                } else if (lookahead.equals("int")) {
                    VarDecl();
                } else {
                    Stmt();
                }
            }
            next();
            mark("Block");
        }
    }

    public void Stmt() {
        ancestor.push(children.size());
        if (lookahead.equals("if")) {
            next();
            if (lookahead.equals("(")) {
                next();
            }
            Cond();
            if (lookahead.equals(")")) {
                next();
            } else {
                error(getRow(index - 1), "j");
            }
            Stmt();
            if (lookahead.equals("else")) {
                next();
                Stmt();
            }
        } else if (lookahead.equals("while")) {
            loop++;
            next();
            if (lookahead.equals("(")) {
                next();
            }
            Cond();
            if (lookahead.equals(")")) {
                next();
            } else {
                error(getRow(index - 1), "j");
            }
            Stmt();
            loop--;
        } else if (lookahead.equals("break")) {
            if (loop == 0) {
                error(getRow(index), "m");
            }
            next();
            if (!lookahead.equals(";")) {
                error(getRow(index - 1), "i");
            } else {
                next();
            }
        } else if (lookahead.equals("continue")) {
            if (loop == 0) {
                error(getRow(index), "m");
            }
            next();
            if (!lookahead.equals(";")) {
                error(getRow(index - 1), "i");
            } else {
                next();
            }
        } else if (lookahead.equals("return")) {
            int row = getRow(index);
            next();
            if (lookahead.equals(";")) {
                next();
            } else if (lookahead.equals("INTCON") || lookahead.equals("IDENFR") ||
                    lookahead.equals("(") || lookahead.equals("+") ||
                    lookahead.equals("-") || lookahead.equals("!")) {
                Exp();
                if (funcHasRet == 0) {
                    error(row, "f");
                }
                if (!lookahead.equals(";")) {
                    error(getRow(index - 1), "i");
                } else {
                    next();
                }
            } else {
                error(getRow(index - 1), "i");
            }
        } else if (lookahead.equals("printf")) {
            int row = getRow(index);
            next();
            if (lookahead.equals("(")) {
                next();
            }
            int numberOfExp = 0;
            if (lookahead.equals("STRCON")) {
                String string = wordList.get(index).getMe();
                for (int i = 0; i < string.length() - 1; i++) {
                    if (string.substring(i, i + 2).equals("%d")) {
                        numberOfExp++;
                    }
                }
                if (!checkString(string)) {
                    error(getRow(index), "a");
                }
                next();
            }
            while (lookahead.equals(",")) {
                next();
                Exp();
                numberOfExp--;
            }
            if (numberOfExp != 0) {
                error(row, "l");
            }
            if (!lookahead.equals(")")) {
                error(getRow(index - 1), "j");
            } else {
                next();
            }
            if (!lookahead.equals(";")) {
                error(getRow(index - 1), "i");
            } else {
                next();
            }
        } else if (lookahead.equals("{")) {
            Block();
        } else if (lookahead.equals(";")) {
            next();
        } else {
            int i = index;
            int row = getRow(i);
            while (!(read(i).equals("=") || read(i).equals(";")
                    || i >= wordList.size() - 1 || getRow(i) > row + 2)) {
                i = i + 1;
            }
            if (read(i).equals("=") &&
                    (lookahead.equals("IDENFR") && (read(index + 1).equals("[") ||
                            read(index + 1).equals("=")))) {
                int start = index;
                LVal();
                if (lookahead.equals("=")) {
                    next();
                    if (lookahead.equals("getint")) {
                        next();
                        if (lookahead.equals("(")) {
                            next();
                        }
                        if (!lookahead.equals(")")) {
                            error(getRow(index - 1), "j");
                        } else {
                            next();
                        }
                        if (!lookahead.equals(";")) {
                            error(getRow(index - 1), "i");
                        } else {
                            next();
                        }
                    } else {
                        Exp();
                        if (!lookahead.equals(";")) {
                            error(getRow(index - 1), "i");
                        } else {
                            next();
                        }
                    }
                } else {
                    children.pop();
                    index = start;
                    Exp();
                    if (!lookahead.equals(";")) {
                        error(getRow(index - 1), "i");
                    } else {
                        next();
                    }
                }
            } else {
                Exp();
                if (!lookahead.equals(";")) {
                    error(getRow(index - 1), "i");
                } else {
                    next();
                }
            }
        }
        mark("Stmt");
    }


    public boolean checkString(String string) {
        for (int i = 0; i < string.length(); i++) {
            int ascii = string.charAt(i);
            if (!(ascii >= 40 && ascii <= 126 && ascii != 92 || ascii == 32 || ascii == 33 || ascii == 34
                    || (ascii == 92 && i != string.length() - 1 && string.charAt(i + 1) == 110)
                    || (ascii == 37 && i != string.length() - 1 && string.charAt(i + 1) == 100))) {
                return false;
            }
        }
        return true;
    }

    public void ConstDecl() {
        ancestor.push(children.size());
        if (lookahead.equals("const")) {
            next();
            if (lookahead.equals("int")) {
                next();
                ConstDef();
                while (lookahead.equals(",")) {
                    next();
                    ConstDef();
                }
                if (lookahead.equals(";")) {
                    next();
                    mark("ConstDecl");
                } else {
                    error(getRow(index - 1), "i");
                    mark("ConstDecl");
                }
            }
        }
    }

    public void ConstDef() {
        ancestor.push(children.size());
        if (lookahead.equals("IDENFR")) {
            next();
            while (lookahead.equals("[")) {
                next();
                ConstExp();
                if (lookahead.equals("]")) {
                    next();
                } else {
                    error(getRow(index - 1), "k");
                }
            }
            if (lookahead.equals("=")) {
                next();
                ConstInitVal();
                mark("ConstDef");
            }
        }
    }

    public void VarDecl() {
        ancestor.push(children.size());
        if (lookahead.equals("int")) {
            next();
            VarDef();
            while (lookahead.equals(",")) {
                next();
                VarDef();
            }
            if (lookahead.equals(";")) {
                next();
                mark("VarDecl");
            } else {
                error(getRow(index - 1), "i");
                mark("VarDecl");
            }
        }
    }

    public void VarDef() {
        ancestor.push(children.size());
        if (lookahead.equals("IDENFR")) {
            next();
            while (lookahead.equals("[")) {
                next();
                ConstExp();
                if (lookahead.equals("]")) {
                    next();
                } else {
                    error(getRow(index - 1), "k");
                }
            }

            if (lookahead.equals("=")) {
                next();
                if (lookahead.equals("getint")) {
                    next(3);
                } else {
                    InitVal();
                }
                mark("VarDef");
            } else {
                mark("VarDef");
            }
        }
    }

    public void ConstInitVal() {
        ancestor.push(children.size());
        if (lookahead.equals("{")) {
            next();
            if (lookahead.equals("}")) {
                next();
                mark("ConstInitVal");
                return;
            }
            ConstInitVal();
            while (lookahead.equals(",")) {
                next();
                ConstInitVal();
            }
            if (lookahead.equals("}")) {
                next();
                mark("ConstInitVal");
            }
        } else {
            ConstExp();
            mark("ConstInitVal");
        }
    }

    public void InitVal() {
        ancestor.push(children.size());
        if (lookahead.equals("{")) {
            next();
            if (lookahead.equals("}")) {
                next();
                mark("InitVal");
                return;
            }
            InitVal();
            while (lookahead.equals(",")) {
                next();
                InitVal();
            }
            if (lookahead.equals("}")) {
                next();
                mark("InitVal");
            }
        } else {
            Exp();
            mark("InitVal");
        }
    }

    //各种表达式
    public void Exp() {
        ancestor.push(children.size());
        AddExp();
        mark("Exp");
    }

    public void Cond() {
        ancestor.push(children.size());
        LOrExp();
        mark("Cond");
    }

    public void LVal() {
        ancestor.push(children.size());
        if (lookahead.equals("IDENFR")) {
            next();
            while (lookahead.equals("[")) {
                next();
                Exp();
                if (lookahead.equals("]")) {
                    next();
                } else {
                    error(getRow(index - 1), "k");
                }
            }
        }
        mark("LVal");
    }

    public void PrimaryExp() {
        ancestor.push(children.size());
        if (lookahead.equals("(")) {
            next();
            Exp();
            if (lookahead.equals(")")) {
                next();
            }
        } else if (lookahead.equals("INTCON")) {
            Number();
        } else if (lookahead.equals("IDENFR")) {
            LVal();
        }
        mark("PrimaryExp");
    }

    public void Number() {
        ancestor.push(children.size());
        if (lookahead.equals("INTCON")) {
            next();
            mark("Number");
        }
    }

    public void FuncFParams() {
        ancestor.push(children.size());
        FuncFParam();
        while (lookahead.equals(",")) {
            next();
            FuncFParam();
        }
        mark("FuncFParams");
    }

    public void FuncRParams() {
        ancestor.push(children.size());
        Exp();
        while (lookahead.equals(",")) {
            next();
            Exp();
        }
        mark("FuncRParams");
    }

    public void FuncFParam() {
        ancestor.push(children.size());
        if (lookahead.equals("int")) {
            next();
            if (lookahead.equals("IDENFR")) {
                next();
                if (lookahead.equals("[")) {
                    next();
                    if (lookahead.equals("]")) {
                        next();
                    } else {
                        error(getRow(index - 1), "k");
                    }
                    if (lookahead.equals("[")) {
                        next();
                        ConstExp();
                        if (lookahead.equals("]")) {
                            next();
                        } else {
                            error(getRow(index - 1), "k");
                        }
                    }
                }
                mark("FuncFParam");
            }
        }
    }

    public void UnaryOp() {
        ancestor.push(children.size());
        if (lookahead.equals("+") || lookahead.equals("-") || lookahead.equals("!")) {
            next();
            mark("UnaryOp");
        }
    }

    public void UnaryExp() {
        ancestor.push(children.size());
        if (lookahead.equals("IDENFR") && read(index + 1).equals("(")) {
            next(2);
            if (lookahead.equals(")")) {
                next();
            } else if (lookahead.equals("INTCON") || lookahead.equals("IDENFR") ||
                    lookahead.equals("(") || lookahead.equals("+") ||
                    lookahead.equals("-") || lookahead.equals("!")) {
                FuncRParams();
                if (!lookahead.equals(")")) {
                    error(getRow(index - 1), "j");
                } else {
                    next();
                }
            } else {
                error(getRow(index - 1), "j");
            }
            mark("UnaryExp");
        } else if (lookahead.equals("+") || lookahead.equals("-") || lookahead.equals("!")) {
            UnaryOp();
            UnaryExp();
            mark("UnaryExp");
        } else {
            PrimaryExp();
            mark("UnaryExp");
        }
    }

    public void MulExp() {
        int start = children.size();
        ancestor.push(children.size());
        UnaryExp();
        mark("MulExp");
        while (lookahead.equals("*") || lookahead.equals("/")
                || lookahead.equals("%") || lookahead.equals("bitand")) {
            ancestor.push(start);
            next();
            UnaryExp();
            mark("MulExp");
        }
    }

    public void AddExp() {
        int start = children.size();
        ancestor.push(children.size());
        MulExp();
        mark("AddExp");

        while (lookahead.equals("+") || lookahead.equals("-")) {
            ancestor.push(start);
            next();
            MulExp();
            mark("AddExp");
        }
    }

    public void RelExp() {
        int start = children.size();
        ancestor.push(children.size());
        AddExp();
        mark("RelExp");

        while (lookahead.equals(">") || lookahead.equals("<") ||
                lookahead.equals(">=") || lookahead.equals("<=")) {
            ancestor.push(start);
            next();
            AddExp();
            mark("RelExp");
        }
    }

    public void EqExp() {
        int start = children.size();
        ancestor.push(children.size());
        RelExp();
        mark("EqExp");

        while (lookahead.equals("==") || lookahead.equals("!=")) {
            ancestor.push(start);
            next();
            RelExp();
            mark("EqExp");
        }
    }

    public void LAndExp() {
        int start = children.size();
        ancestor.push(children.size());
        EqExp();
        mark("LAndExp");

        while (lookahead.equals("&&")) {
            ancestor.push(start);
            next();
            EqExp();
            mark("LAndExp");
        }
    }

    public void LOrExp() {
        int start = children.size();
        ancestor.push(children.size());
        LAndExp();
        mark("LOrExp");

        while (lookahead.equals("||")) {
            ancestor.push(start);
            next();
            LAndExp();
            mark("LOrExp");
        }
    }

    //注：使用的Ident 必须是常量
    public void ConstExp() {
        ancestor.push(children.size());
        AddExp();
        mark("ConstExp");
    }

    public void next() {
        if (index != -1 && lookahead.equals("IDENFR")) {
            lookahead = wordList.get(index).getMe();
        }
        if (index != -1 && lookahead.equals("INTCON")) {
            lookahead = wordList.get(index).getMe();
        }
        Node node = new Node(index, lookahead);
        children.push(node);

        index += 1;
        if (index != wordList.size()) {
            lookahead = read(index);
        }
    }

    public void next(int num) {
        for (int i = 0; i < num; i++) {
            next();
        }
    }

    public String read(int index) {
        String word = wordList.get(index).getType();
        if (dict.containsValue(word)) {
            word = wordList.get(index).getMe();
        }
        return word;
    }

    public void mark(String string) {
        partList.add(new Part(index - 1, string));
        if (index > 0 && string.equals("UnaryOp")) {
            string = wordList.get(index - 1).getMe();
        }
        if (index > 0 && string.equals("Number")) {
            string = wordList.get(index - 1).getMe();
        }

        Node node = new Node(index - 1, string);
        int end = children.size();
        for (int i = ancestor.pop(); i < end; i++) {
            node.addChild(children.pop());
        }
        children.push(node);
    }

    public int getRow(int index) {
        return wordList.get(index).getRow();
    }

    public void error(int row, String type) {
        errorList.add(new Pair<Integer, String>(row, type));
        //System.out.print(row + " " + type + "Error in word " + index + "\n");
        if (type.equals("j")) {
            Node node = new Node(index, ")");
            children.push(node);
        } else if (type.equals("k")) {
            Node node = new Node(index, "]");
            children.push(node);
        } else if (type.equals("i")) {
            Node node = new Node(index, ";");
            children.push(node);
        }
    }

}
